/**

 * @author admin

 * 2014年7月15日

 */

package com.tmall.testgen.client.runinspring;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.net.URL;
import java.net.URLClassLoader;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;

import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.Modifier;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.MethodInfo;

import com.tmall.testgen.common.util.MapEntry;
import com.tmall.testgen.common.util.Object2TreeMap;
import com.tmall.testgen.common.util.bo.FieldClassAndValue;
import com.tmall.testgen.plugin.views.vo.SpringTree;
import com.tmall.testgen.plugin.views.vo.TreeObject;

public class RunInSpringUtil {
	public static Object getValue(SpringTree root) throws Exception{
//		if(root.getValue()!=null)
//			return root.getValue();
		
		if(root.getChildren()==null||root.getChildren().length==0){
			return root.getValue();
		}
		if(Collection.class.isAssignableFrom(root.getClazz())){
			return  getCollectionValue(root);
		}
		if(Map.class.isAssignableFrom(root.getClazz())){
			return getMapValue(root);
		}
		
		return getObjectValue(root);
	}
	private static Object getMapValue(SpringTree root) throws Exception{
		Map map=null;
		try{
			map=(Map) root.getClazz().newInstance();
		}catch(Exception e){
			return null;
		}
		
		
		TreeObject[] children = root.getChildren();
		for(TreeObject to:children){
			
			SpringTree st=(SpringTree) to;
			MapEntry entry=(MapEntry) getValue(st);
			map.put(entry.getKey(), entry.getValue());
		}
		return map;
	}
	private static Object getCollectionValue(SpringTree root) throws Exception{
		Collection result=null;
		try{
			result=(Collection) root.getClazz().newInstance();
		}catch(Exception e){
			return null;
		}
				
		 
		TreeObject[] children = root.getChildren();
		for(TreeObject to:children){
			SpringTree st=(SpringTree) to;
			result.add(getValue(st));
		}
		return result;
	}
	
	private static Object getObjectValue(SpringTree root) throws Exception{
		if(isBasicType(root.getClazz())){
			return root.getValue();
		}
		
		Object result= null;
		try{
			root.setClazz(Class.forName(root.getClazz().getName(),true,RunInSpringHandler.getClassLoader()));
			result=root.getClazz().newInstance();
		}catch(Exception e){
			return null;
		}
		
		TreeObject[] children = root.getChildren();
		for(TreeObject to:children){
			
			
			SpringTree st=(SpringTree) to;
			Object value=getValue(st);
			setFieldValue(st.getFieldName(),result, value);
//			Field field=root.getClazz().getDeclaredField(st.getFieldName());
//			field.setAccessible(true);
//			field.set(result, value);
		}
		
		return result;
	}
	
	
	
	private static  void setFieldValue(String fieldName,Object target,Object value) throws Exception{
		Class clazz=target.getClass();
        for(; clazz != Object.class&&clazz!=null ; clazz = clazz.getSuperclass()) {  
        	Field[] fields=clazz.getDeclaredFields();
        	for(Field field:fields){
        		if(field.getName().equals(fieldName)){
        			field.setAccessible(true);
        			field.set(target, value);
        		}
        			
    		}
        	
        }  
	}
	
//	public static void main(String[] args) throws Exception {
//	    Method m=Test.class.getMethod("test", Integer.class);
//	    System.out.println(getMethodShowName(m));
//    }
	
	public static  String getMethodShowName(Method method) throws Exception {
		String className = method.getDeclaringClass().getName();

		ClassPool  pool = new ClassPool(null);
		pool.appendSystemPath();
         
         
		pool.insertClassPath(new ClassClassPath(method.getDeclaringClass()));
		Class[] parameterClasses = method.getParameterTypes();
		CtClass[] parameterCtClasses = new CtClass[parameterClasses.length];

		for (int i = 0; i < parameterClasses.length; i++) {
			parameterCtClasses[i] = pool.get(parameterClasses[i].getName());
		}

		CtClass ctClass = pool.get(className);
		CtMethod ctMethod = ctClass.getDeclaredMethod(method.getName(),
				parameterCtClasses);

		MethodInfo methodInfo = ctMethod.getMethodInfo();
		int ttts = methodInfo.getLineNumber(-1);
		CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
		LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute
				.getAttribute(LocalVariableAttribute.tag);

		int length = method.getParameterTypes().length;
		List<String> paramNames = new ArrayList<String>();
		int pos = Modifier.isStatic(method.getModifiers()) ? 0 : 1;
		if (attr != null) {
			for (int i = 0; i < length; i++) {
				paramNames.add(attr.variableName(i + pos));
			}
		}

		
		StringBuilder snap = new StringBuilder();
		for (int i = 0; i < parameterClasses.length; i++) {
			if (i > 0) {
				snap.append(",");
			}
			String parasName = parameterClasses[i].getSimpleName() + " "
					+ paramNames.get(i);
			snap.append(parasName);

		}
		return className + "." + method.getName() + "(" + snap.toString()
				+ "):" + method.getReturnType().getSimpleName();
	}
	
	
	
	
	public static boolean isBasicType(Class clazz){
		if(clazz==null)
			return true;
		
		if( String.class.isAssignableFrom(clazz) ||Date.class.isAssignableFrom(clazz)||BigDecimal.class.isAssignableFrom(clazz))
			return true;
		
		if(Boolean.class.isAssignableFrom(clazz) ||clazz.getName().equals("boolean"))
			return true;
		if(Byte.class.isAssignableFrom(clazz) ||clazz.getName().equals("byte") )
			return true;
		if(Short.class.isAssignableFrom(clazz) ||clazz.getName().equals("short"))
			return true;
		if(Integer.class.isAssignableFrom(clazz)||clazz.getName().equals("int") )
			return true;
		if(Long.class.isAssignableFrom(clazz)||clazz.getName().equals("long"))
			return true;
		if(Float.class.isAssignableFrom(clazz) ||clazz.getName().equals("float"))
			return true;
		if(Double.class.isAssignableFrom(clazz) ||clazz.getName().equals("double"))
			return true;
		
		
		
		return false;
		
	}
	
	public static Object getValueByClass(Class clazz,String value) throws Exception{
		if(clazz==null||value.length()==0)
			return null;
		
		
		if(Boolean.class.isAssignableFrom(clazz) )
			return Boolean.valueOf(value);
		if(Byte.class.isAssignableFrom(clazz) )
			return Byte.valueOf(value);
		if(Short.class.isAssignableFrom(clazz) )
			return Short.valueOf(value);
		if(Integer.class.isAssignableFrom(clazz) )
			return Integer.valueOf(value);
		if(Long.class.isAssignableFrom(clazz))
			return Long.valueOf(value);
		if(Float.class.isAssignableFrom(clazz) )
			return Float.valueOf(value);
		if(Double.class.isAssignableFrom(clazz) )
			return Double.valueOf(value);
		if(Date.class.isAssignableFrom(clazz)){
	        DateFormat sf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
	        return sf.parse(value);
		}
		if(BigDecimal.class.isAssignableFrom(clazz)){
		    return BigDecimal.valueOf(Double.valueOf(value));
		}
		
		return value;
	}
	
	
	public static Object getContentResult(SpringTree currentTree,String content) throws Exception {
		String defineCode=currentTree.getClazz().getName()+" "+currentTree.getFieldName()+";";
		String returnCode="return ($w)"+currentTree.getFieldName()+";";
		content=defineCode+content+returnCode;
		ClassPool  defaultPool = new ClassPool(null);
        defaultPool.appendSystemPath();
		
		URLClassLoader  cl=RunInSpringHandler.getClassLoader();
		URL [] urls=cl.getURLs();
		for(URL url:urls){
			defaultPool.insertClassPath(url.getPath());
		}
		
		
       defaultPool.insertClassPath(new ClassClassPath(RunInSpringUtil.class));
		CtClass ctClass = defaultPool.get(RunInSpringUtil.class.getName());  
		
		
		ctClass.defrost();
		CtMethod  ctMethod=ctClass.getDeclaredMethod("dynamicRun");
		ctMethod.setBody("{"+content+"}");
		
		Object target= ctClass.toClass(new URLClassLoader(new URL[0], RunInSpringHandler.getClassLoader())).newInstance();
		Method method=target.getClass().getMethod("dynamicRun");
		return method.invoke(target);
		
	
	}
	public Object dynamicRun(){
		return null;
	}
	
	public static void addValue2Tree(SpringTree tree,Object value){
		Map<String, FieldClassAndValue>  fieldValues=Object2TreeMap.getMapFromObject(value,true);
		if(fieldValues==null)
			return ;
		for(String name:fieldValues.keySet()){
			
			SpringTree node = new SpringTree();
			
			
			
			node.setClazz(fieldValues.get(name).getClazz());
			
			node.setValue(fieldValues.get(name).getValue());
			node.setLeafNode(node.getValue()==null||isBasicType(node.getClazz()));
			node.setFieldName(name);
			node.initName();
			tree.addChild(node);
			
		}
	}
}
